package org.proteored.miapeapi.util;

import org.apache.log4j.Logger;
import org.proteored.miapeapi.cv.Accession;
import org.proteored.miapeapi.cv.ControlVocabularyManager;
import org.proteored.miapeapi.cv.ControlVocabularyTerm;
import org.proteored.miapeapi.cv.ms.ChargeState;
import org.proteored.miapeapi.cv.ms.MOverZ;
import org.proteored.miapeapi.cv.ms.RetentionTime;
import org.proteored.miapeapi.exceptions.IllegalMiapeArgumentException;
import org.proteored.miapeapi.interfaces.Adapter;
import org.proteored.miapeapi.xml.pride.autogenerated.ExperimentType.MzData.SpectrumList.Spectrum;
import org.proteored.miapeapi.xml.pride.autogenerated.ObjectFactory;
import org.proteored.miapeapi.xml.pride.autogenerated.ParamType;
import org.proteored.miapeapi.xml.pride.autogenerated.PeakListBinaryType;
import org.proteored.miapeapi.xml.pride.autogenerated.PeakListBinaryType.Data;
import org.proteored.miapeapi.xml.pride.autogenerated.PrecursorType;
import org.proteored.miapeapi.xml.pride.autogenerated.SpectrumDescType;
import org.proteored.miapeapi.xml.pride.autogenerated.SpectrumDescType.PrecursorList;
import org.proteored.miapeapi.xml.pride.autogenerated.SpectrumSettingsType;
import org.proteored.miapeapi.xml.pride.autogenerated.SpectrumSettingsType.SpectrumInstrument;
import org.proteored.miapeapi.xml.pride.util.PrideControlVocabularyXmlFactory;
import org.proteored.miapeapi.xml.util.peaklistreader.TMsData;
import org.proteored.miapeapi.xml.util.peaklistreader.TSpectrum;

public class SpectumAdapterFromMGFSpectrum implements Adapter<Spectrum> {
	private static Logger log = Logger.getLogger("log4j.logger.org.proteored");

	private final TMsData tMsData;
	private final int queryNumber;
	private final ObjectFactory factory;
	private final ControlVocabularyManager cvManager;
	private final PrideControlVocabularyXmlFactory prideCvUtil;

	private final int offset;

	public SpectumAdapterFromMGFSpectrum(TMsData tMsData, int queryNumber,
			int offset, ObjectFactory spectrumObjectFactory,
			ControlVocabularyManager cvManager) {
		this.tMsData = tMsData;
		this.queryNumber = queryNumber;
		this.offset = offset;
		factory = spectrumObjectFactory;
		this.cvManager = cvManager;
		prideCvUtil = new PrideControlVocabularyXmlFactory(factory, cvManager);
	}

	@Override
	public Spectrum adapt() {
		Spectrum xmlSpectrum = factory
				.createExperimentTypeMzDataSpectrumListSpectrum();
		SpectrumDescType xmlSpecDesc = factory.createSpectrumDescType();
		SpectrumSettingsType xmlSpectrumSettings = factory
				.createSpectrumSettingsType();
		SpectrumInstrument xmlInstrument = factory
				.createSpectrumSettingsTypeSpectrumInstrument();

		TSpectrum spectrum = tMsData.getSpectrum(queryNumber);

		xmlInstrument.setMsLevel(spectrum.getMsLevel());

		if (spectrum.getMzStart() != null)
			xmlInstrument.setMzRangeStart(Float.valueOf(String.valueOf(spectrum
					.getMzStart())));
		if (spectrum.getMzEnd() != null)
			xmlInstrument.setMzRangeStop(Float.valueOf(String.valueOf(spectrum
					.getMzEnd())));
		xmlSpectrumSettings.setSpectrumInstrument(xmlInstrument);
		xmlSpecDesc.setSpectrumSettings(xmlSpectrumSettings);

		ParamType xmlIonSection = factory.createParamType();

		// CHARGE
		// only capture charge if it is defined in the MGF. If not, assume
		// charge=1
		if (spectrum.getPeptideCharge() > 0) {
			ControlVocabularyTerm chargeStateTerm = ChargeState.getInstance(
					cvManager).getChargeStateTerm();
			prideCvUtil.addCvParamToParamType(xmlIonSection,
					chargeStateTerm.getTermAccession(),
					chargeStateTerm.getPreferredName(),
					String.valueOf(tMsData.getPepCharge(queryNumber)),
					chargeStateTerm.getCVRef());
		} else {

			log.debug("Charge state not found in MGF file. Assuming charge 1");
			ControlVocabularyTerm chargeStateTerm = ChargeState.getInstance(
					cvManager).getChargeStateTerm();
			prideCvUtil.addCvParamToParamType(xmlIonSection,
					chargeStateTerm.getTermAccession(),
					chargeStateTerm.getPreferredName(), "1",
					chargeStateTerm.getCVRef());
		}

		// M over z
		ControlVocabularyTerm mOverZTerm = MOverZ.getMOverZTerm(cvManager);
		Double pepMoverZ = tMsData.getPepMoverZ(queryNumber);
		if (pepMoverZ != null) {
			prideCvUtil.addCvParamToParamType(xmlIonSection,
					mOverZTerm.getTermAccession(),
					mOverZTerm.getPreferredName(), String.valueOf(pepMoverZ),
					mOverZTerm.getCVRef());
			// This is to add a line cvParam name="MassToChargeRatio"
			// accession="MS:1000040" value="..."' since it is necessary to add
			// it in the mzData for a MASCOT search. It is a bug from Mascot,
			// since
			// the preferred name is other
			prideCvUtil.addCvParamToParamType(xmlIonSection,
					mOverZTerm.getTermAccession(), "MassToChargeRatio",
					String.valueOf(pepMoverZ), "MS");
		}

		// Selected ion m/z
		ControlVocabularyTerm selectedMOverZTerm = MOverZ
				.getSelected_Ion_MOverZTerm(cvManager);
		if (pepMoverZ != null) {
			prideCvUtil.addCvParamToParamType(xmlIonSection,
					selectedMOverZTerm.getTermAccession(),
					selectedMOverZTerm.getPreferredName(),
					String.valueOf(pepMoverZ), selectedMOverZTerm.getCVRef());
		}
		// Retention time
		Double spectrumRT = tMsData.getRT(queryNumber);
		if (spectrumRT != null) {
			ControlVocabularyTerm rtTerm = RetentionTime.getInstance(cvManager)
					.getCVTermByAccession(
							new Accession(RetentionTime.RetentionTime));
			prideCvUtil.addCvParamToParamType(xmlIonSection,
					rtTerm.getTermAccession(), rtTerm.getPreferredName(),
					String.valueOf(spectrumRT), mOverZTerm.getCVRef());
		}
		// 12-Nov-2011
		// changed
		// int spectrumReference = Utils.getIntegerValue(tMsData
		// .getSpectrumReference(spectrumRef));
		// log.info("SpectrumReference from tMsdata=" + spectrumReference +
		// " and index=" + i);
		int spectrumID = queryNumber + offset;
		// int spectrumID = queryNumber + 1 + offset;

		// Precursor
		// Even if there is not a precursor query number, create ir
		if (spectrum.getMsLevel() > 1) {
			final TSpectrum precursorSpectrum = tMsData.getSpectrum(spectrum
					.getPrecursorQueryNumber());

			PrecursorList xmlPrecursorList = factory
					.createSpectrumDescTypePrecursorList();
			PrecursorType xmlPrecursor = factory.createPrecursorType();

			if (precursorSpectrum != null) {
				xmlPrecursor.setMsLevel(precursorSpectrum.getMsLevel());
				// reference to the MS spectrum
				xmlPrecursor.setSpectrumRef(precursorSpectrum.getQueryNumber()
						+ offset);
			} else {
				xmlPrecursor.setMsLevel(0);
				// reference to the MS spectrum
				xmlPrecursor.setSpectrumRef(0);
			}

			ParamType xmlActivation = factory.createParamType();
			xmlPrecursor.setActivation(xmlActivation);

			xmlPrecursor.setIonSelection(xmlIonSection);
			// information is not in the MGF file at the moment

			xmlPrecursorList.getPrecursor().add(xmlPrecursor);
			xmlPrecursorList.setCount(1);

			xmlSpecDesc.setPrecursorList(xmlPrecursorList);

		}
		xmlSpectrum.setId(spectrumID);
		xmlSpectrum.setSpectrumDesc(xmlSpecDesc);

		// Set intensities
		PeakListBinaryType peakListBinaryArray = factory
				.createPeakListBinaryType();
		Data arrayData = factory.createPeakListBinaryTypeData();
		arrayData.setValue(tMsData
				.getIntensityByteArrayPFFSpectrum(queryNumber));
		arrayData.setEndian(tMsData.getMzArrayEndian(queryNumber));
		arrayData.setLength(Integer.valueOf(tMsData
				.getMzArrayLength(queryNumber)));
		arrayData.setPrecision(tMsData.getMzArrayPrecision(queryNumber));
		if (arrayData.getValue() == null || arrayData.getValue().length == 0) {
			throw new IllegalMiapeArgumentException("Spectrum with empty data");
		}
		peakListBinaryArray.setData(arrayData);
		xmlSpectrum.setIntenArrayBinary(peakListBinaryArray);

		// Set mz values
		peakListBinaryArray = factory.createPeakListBinaryType();
		arrayData = factory.createPeakListBinaryTypeData();
		arrayData.setValue(tMsData.getMzByteArrayPFFSpectrum(queryNumber));
		arrayData.setEndian(tMsData.getMzArrayEndian(queryNumber));
		arrayData.setLength(Integer.valueOf(tMsData
				.getMzArrayLength(queryNumber)));
		arrayData.setPrecision(tMsData.getMzArrayPrecision(queryNumber));
		if (arrayData.getValue() == null || arrayData.getValue().length == 0) {
			throw new IllegalMiapeArgumentException("Spectrum with empty data");
		}
		peakListBinaryArray.setData(arrayData);
		xmlSpectrum.setMzArrayBinary(peakListBinaryArray);

		return xmlSpectrum;
	}
}
